/*----------------------------------------------------------------------------------------
*
*  Copyright 2019, Gao Hai Hui, <fromzeropoint@126.com>.  All rights reserved.
*  https://gitee.com/helloworldghh/xoskit.git
*  Use of this source code is governed by a MIT license
*  that can be found in the License file.
*
----------------------------------------------------------------------------------------*/
#include "../import/head.h"
#include "../xos/head.h"
#include "../os/head.h"
#ifndef XOS_LINUX
#define XOS_LINUX
#endif
#include "../../../../../xosbin/lib/include/iconv/head.h"
#include "../../../../../xosbin/lib/include/uuid/head.h"
#include "misc_impl.h"

/////////////////////////////////////////////////////////////////////////////////////////
// 
namespace xos
{
    ////////////////////////////////////////////////////////////////////////////////////
    // 

    static misc_impl::ITEM_DATA xos_item_data;

    ////////////////////////////////////////////////////////////////////////////////////
    //

    misc_impl::misc_impl()
    {
    }

    misc_impl::~misc_impl()
    {
        term();
    }

    ////////////////////////////////////////////////////////////////////////////////////
    // 

    int misc_impl::get_xos_item_data( ITEM_DATA*& item_ptr )
    {
        int ret = 0;
        xos_item_data.set_container( mgr::container() );
        item_ptr = &xos_item_data;
        return ret;
    }

    int misc_impl::init()
    {
        int nRet = 0;
        return nRet;
    }

    int misc_impl::term()
    {
        int nRet = 0;
        return nRet;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    int misc_impl::utf16_to_wchar( const char * lpszSrc, size_t tSrcSize, char * lpszDst, size_t tDstBufSize, size_t & DstSize )
    {
        int ret = 0;

        size_t out_buf_size = (size_t)tDstBufSize;
        char * out_buf = lpszDst;

        wchar_t * lpszResult = (wchar_t *)out_buf;
        size_t tResultLen = 0;

        size_t in_size = (size_t)tSrcSize;
        char * in_buf = (char *)lpszSrc;

        iconv_t cd = (iconv_t)-1;

        if( 0 == ret )
        {
            bool bSmall = mgr::container()->common_misc()->is_small_endian();
            const char * lpszDstType = bSmall ? "UTF-16LE" : "UTF-16BE";
            cd = iconv_open( "WCHAR_T", lpszDstType );
            if( cd == (iconv_t)-1 )
            {
                ret = 1;
            }
        }

        if( 0 == ret )
        {
            size_t tOutLen = out_buf_size;
            size_t tInLen = in_size;
            char * pOut = out_buf;
            char * pIn = in_buf;
            size_t tLen = iconv( cd, &pIn, &tInLen, &pOut, &tOutLen );
            if( tLen == (size_t)-1 )
            {
                ret = 2;
            }
            else
            {
                DstSize = out_buf_size - tOutLen;
            }
        }

        if( 0 == ret )
        {
            tResultLen = DstSize / sizeof( wchar_t );
            lpszResult[tResultLen] = 0;
        }

        if( (iconv_t)-1 != cd )
        {
            iconv_close( cd );
            cd = (iconv_t)-1;
        }

        return ret;
    }

    int misc_impl::wchar_to_utf16( const char * lpszSrc, size_t tSrcSize, char * lpszDst, size_t tDstBufSize, size_t & DstSize )
    {
        int ret = 0;

        size_t out_buf_size = (size_t)tDstBufSize;
        char * out_buf = lpszDst;

        int16_t * lpszResult = (int16_t *)out_buf;
        size_t tResultLen = 0;

        size_t in_size = (size_t)tSrcSize;
        char * in_buf = (char *)lpszSrc;

        iconv_t cd = (iconv_t)-1;

        if( 0 == ret )
        {
            bool bSmall = mgr::container()->common_misc()->is_small_endian();
            const char * lpszDstType = bSmall ? "UTF-16LE" : "UTF-16BE";
            cd = iconv_open( lpszDstType, "WCHAR_T" );
            if( cd == (iconv_t)-1 )
            {
                ret = 1;
            }
        }

        if( 0 == ret )
        {
            size_t tOutLen = out_buf_size;
            size_t tInLen = in_size;
            char * pOut = out_buf;
            char * pIn = in_buf;
            size_t tLen = iconv( cd, &pIn, &tInLen, &pOut, &tOutLen );
            if( tLen == (size_t)-1 )
            {
                ret = 2;
            }
            else
            {
                DstSize = out_buf_size - tOutLen;
            }
        }

        if( 0 == ret )
        {
            tResultLen = DstSize / sizeof( int16_t );
            lpszResult[tResultLen] = 0;
        }

        if( (iconv_t)-1 != cd )
        {
            iconv_close( cd );
            cd = (iconv_t)-1;
        }

        return ret;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    int misc_impl::utf8_to_wchar( const char * lpszSrc, size_t tSrcSize, char * lpszDst, size_t tDstBufSize, size_t & DstSize )
    {
        int ret = 0;

        size_t out_buf_size = (size_t)tDstBufSize;
        char * out_buf = lpszDst;

        wchar_t * lpszResult = (wchar_t *)out_buf;
        size_t tResultLen = 0;

        size_t in_size = (size_t)tSrcSize;
        char * in_buf = (char *)lpszSrc;

        iconv_t cd = (iconv_t)-1;

        if( 0 == ret )
        {
            cd = iconv_open( "WCHAR_T", "UTF-8" );
            if( cd == (iconv_t)-1 )
            {
                ret = 1;
            }
        }

        if( 0 == ret )
        {
            size_t tOutLen = out_buf_size;
            size_t tInLen = in_size;
            char * pOut = out_buf;
            char * pIn = in_buf;
            size_t tLen = iconv( cd, &pIn, &tInLen, &pOut, &tOutLen );
            if( tLen == (size_t)-1 )
            {
                ret = 2;
            }
            else
            {
                DstSize = out_buf_size - tOutLen;
            }
        }

        if( 0 == ret )
        {
            tResultLen = DstSize / sizeof( wchar_t );
            lpszResult[tResultLen] = 0;
        }

        if( (iconv_t)-1 != cd )
        {
            iconv_close( cd );
            cd = (iconv_t)-1;
        }

        return ret;
    }

    int misc_impl::wchar_to_utf8( const char * lpszSrc, size_t tSrcSize, char * lpszDst, size_t tDstBufSize, size_t & DstSize )
    {
        int ret = 0;

        size_t out_buf_size = (size_t)tDstBufSize;
        char * out_buf = lpszDst;

        char * lpszResult = (char *)out_buf;
        size_t tResultLen = 0;

        size_t in_size = (size_t)tSrcSize;
        char * in_buf = (char *)lpszSrc;

        iconv_t cd = (iconv_t)-1;

        if( 0 == ret )
        {
            cd = iconv_open( "UTF-8", "WCHAR_T" );
            if( cd == (iconv_t)-1 )
            {
                ret = 1;
            }
        }

        if( 0 == ret )
        {
            size_t tOutLen = out_buf_size;
            size_t tInLen = in_size;
            char * pOut = out_buf;
            char * pIn = in_buf;
            size_t tLen = iconv( cd, &pIn, &tInLen, &pOut, &tOutLen );
            if( tLen == (size_t)-1 )
            {
                ret = 2;
            }
            else
            {
                DstSize = out_buf_size - tOutLen;
            }
        }

        if( 0 == ret )
        {
            tResultLen = DstSize;
            lpszResult[tResultLen] = 0;
        }

        if( (iconv_t)-1 != cd )
        {
            iconv_close( cd );
            cd = (iconv_t)-1;
        }

        return ret;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    int misc_impl::utf8_to_utf16( const char * lpszSrc, size_t tSrcSize, char * lpszDst, size_t tDstBufSize, size_t & DstSize )
    {
        int ret = 0;

        size_t out_buf_size = (size_t)tDstBufSize;
        char * out_buf = lpszDst;

        int16_t * lpszResult = (int16_t *)out_buf;
        size_t tResultLen = 0;

        size_t in_size = (size_t)tSrcSize;
        char * in_buf = (char *)lpszSrc;

        iconv_t cd = (iconv_t)-1;

        if( 0 == ret )
        {
            bool bSmall = mgr::container()->common_misc()->is_small_endian();
            const char * lpszDstType = bSmall ? "UTF-16LE" : "UTF-16BE";
            cd = iconv_open( lpszDstType, "UTF-8" );
            if( cd == (iconv_t)-1 )
            {
                ret = 1;
            }
        }

        if( 0 == ret )
        {
            size_t tOutLen = out_buf_size;
            size_t tInLen = in_size;
            char * pOut = out_buf;
            char * pIn = in_buf;
            size_t tLen = iconv( cd, &pIn, &tInLen, &pOut, &tOutLen );
            if( tLen == (size_t)-1 )
            {
                ret = 2;
            }
            else
            {
                DstSize = out_buf_size - tOutLen;
            }
        }

        if( 0 == ret )
        {
            tResultLen = DstSize / sizeof( int16_t );
            lpszResult[tResultLen] = 0;
        }

        if( (iconv_t)-1 != cd )
        {
            iconv_close( cd );
            cd = (iconv_t)-1;
        }

        return ret;
    }

    int misc_impl::utf16_to_utf8( const char * lpszSrc, size_t tSrcSize, char * lpszDst, size_t tDstBufSize, size_t & DstSize )
    {
        int ret = 0;

        size_t out_buf_size = (size_t)tDstBufSize;
        char * out_buf = lpszDst;

        char * lpszResult = (char *)out_buf;
        size_t tResultLen = 0;

        size_t in_size = (size_t)tSrcSize;
        char * in_buf = (char *)lpszSrc;

        iconv_t cd = (iconv_t)-1;

        if( 0 == ret )
        {
            bool bSmall = mgr::container()->common_misc()->is_small_endian();
            const char * lpszDstType = bSmall ? "UTF-16LE" : "UTF-16BE";
            cd = iconv_open( "UTF-8", lpszDstType );
            if( cd == (iconv_t)-1 )
            {
                ret = 1;
            }
        }

        if( 0 == ret )
        {
            size_t tOutLen = out_buf_size;
            size_t tInLen = in_size;
            char * pOut = out_buf;
            char * pIn = in_buf;
            size_t tLen = iconv( cd, &pIn, &tInLen, &pOut, &tOutLen );
            if( tLen == (size_t)-1 )
            {
                ret = 2;
            }
            else
            {
                DstSize = out_buf_size - tOutLen;
            }
        }

        if( 0 == ret )
        {
            tResultLen = DstSize;
            lpszResult[tResultLen] = 0;
        }

        if( (iconv_t)-1 != cd )
        {
            iconv_close( cd );
            cd = (iconv_t)-1;
        }

        return ret;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    // 
    // utf8 to utf16
    // 
    int misc_impl::utf8_to_utf16( const xos_box::i_string * pSrc, xos_box::i_buf * pDstBuf )
    {
        char * out_buf = pDstBuf->get_data( 0, 0, 0, 0 );
        size_t out_buf_size = pDstBuf->get_buf_size( 0 );
        size_t out_size = 0;

        const char * in_buf = pSrc->get( 0, 0 );
        size_t in_size = pSrc->length();

        int ret = 0;

        if( 0 == ret )
        {
            ret = utf8_to_utf16( in_buf, in_size, out_buf, out_buf_size, out_size );
        }

        if( 0 == ret )
        {
            pDstBuf->set_len( (int)out_size );
        }

        return ret;
    }

    // 
    // utf16 utf8
    // 
    int misc_impl::utf16_to_utf8( xos_box::i_buf * pSrcBuf, xos_box::i_string * pDst )
    {
        xos_box::i_buf * pBuf = mgr::container()->box()->buf();

        char * out_buf = pBuf->get_data( 0, 0, 0, 0 );
        size_t out_buf_size = pBuf->get_buf_size( 0 );
        size_t out_size = 0;

        char * lpszResult = (char *)out_buf;
        size_t tResultLen = 0;

        const char * in_buf = (const char *)pSrcBuf->get_data( 0, 0, 0, 0 );
        size_t in_size = pSrcBuf->get_len( 0 );

        int ret = 0;

        if( 0 == ret )
        {
            ret = utf16_to_utf8( in_buf, in_size, out_buf, out_buf_size, out_size );
        }

        if( 0 == ret )
        {
            tResultLen = out_size;
            pDst->set( lpszResult, tResultLen );
        }

        xos_stl::release_interface( pBuf );

        return ret;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    // 
    // utf8 to wchar
    // 
    int misc_impl::utf8_to_wchar( const xos_box::i_string * pSrc, xos_box::i_wstring * pDst )
    {
        int ret = 0;

        xos_box::i_buf * pBuf = mgr::container()->box()->buf();

        char * out_buf = pBuf->get_data( 0, 0, 0, 0 );
        size_t out_buf_size = pBuf->get_buf_size( 0 );
        size_t out_size = 0;

        wchar_t * lpszResult = (wchar_t *)out_buf;
        size_t tResultLen = 0;

        const char * in_buf = (const char *)pSrc->get( 0, 0 );
        size_t in_size = pSrc->length();

        if( 0 == ret )
        {
            ret = utf8_to_wchar( in_buf, in_size, out_buf, out_buf_size, out_size );
        }

        if( 0 == ret )
        {
            tResultLen = out_size / sizeof( wchar_t );
            pDst->set( lpszResult, tResultLen );
        }

        xos_stl::release_interface( pBuf );

        return ret;
    }

    // 
    // wcharto utf8
    // 
    int misc_impl::wchar_to_utf8( const xos_box::i_wstring * pSrc, xos_box::i_string * pDst )
    {
        int ret = 0;

        xos_box::i_buf * pBuf = mgr::container()->box()->buf();

        char * out_buf = pBuf->get_data( 0, 0, 0, 0 );
        size_t out_buf_size = pBuf->get_buf_size( 0 );
        size_t out_size = 0;

        char * lpszResult = (char *)out_buf;
        size_t tResultLen = 0;

        const char * in_buf = (const char *)pSrc->get( 0, 0 );
        size_t in_size = pSrc->length() * 2;

        if( 0 == ret )
        {
            ret = wchar_to_utf8( in_buf, in_size, out_buf, out_buf_size, out_size );
        }

        if( 0 == ret )
        {
            tResultLen = out_size;
            pDst->set( lpszResult, tResultLen );
        }

        xos_stl::release_interface( pBuf );

        return ret;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    // 
    // get process full file name
    // 
    int misc_impl::get_process_file_full_name( char * pszFile, int nLen, int * pnLenNeeded )
    {
        int nRet = 0;

        char szFile[4096] = { 0 };
        int len = 0;

        os::get_exe_full_file( szFile, sizeof( szFile ) / sizeof( szFile[0] ) );

        len = strlen( szFile ) + 1;

        if( pszFile && ( nLen > len ) )
        {
            strcpy( pszFile, szFile );
        }

        if( pnLenNeeded )
        {
            *pnLenNeeded = len + 1;
        }

        return nRet;
    }

    int misc_impl::get_process_file_full_name( xos_box::i_string * pStr )
    {
        int ret = 0;

        xos_box::i_big_buf * pBuf = mgr::container()->box()->big_buf();
        char * pB = pBuf->get_data( 0, 0, 0, 0 );
        int nSize = pBuf->get_buf_size( 0 );
        {
            os::get_exe_full_file( pB, nSize );
            pStr->set( pB );
        }
        xos_stl::release_interface( pBuf );

        return ret;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    // 
    // get process file path. including '\\' or '/'
    //
    int misc_impl::get_process_path( char * pszPath, int nLen, int * pnLenNeeded )
    {
        int nRet = 0;

        char szFile[4096] = { 0 };

        os::get_exe_full_file( szFile, sizeof( szFile ) / sizeof( szFile[0] ) );

        char * p = 0, * pp = szFile;

        while( *pp != 0 )
        {
            if( *pp == '/' )
            {
                p = pp;
            }

            ++pp;
        }

        if( p )
        {
            *p = 0;
        }

        int len = ( int )strlen( szFile );

        if( pnLenNeeded )
        {
            *pnLenNeeded = len;
        }

        if( pszPath && ( nLen > len ) )
        {
            strcpy( pszPath, szFile );
        }
        else
        {
            nRet = 1;
        }

        return nRet;
    }

    // 
    // get process file path. including '\\'
    //
    int misc_impl::get_process_path( xos_box::i_string * pStr )
    {
        int ret = 0;

        xos_box::i_big_buf * pBuf = mgr::container()->box()->big_buf();
        char * pB = pBuf->get_data( 0, 0, 0, 0 );
        int nSize = pBuf->get_buf_size( 0 );

        {
            os::get_exe_full_file( pB, nSize );
        }
        {
            char * p = 0, * pp = pB;
            while( *pp != 0 )
            {
                if( *pp == '/' )
                {
                    p = pp;
                }
                ++pp;
            }
            if( p )
            {
                *p = 0;
            }
        }
        {
            pStr->set( pB );
        }

        xos_stl::release_interface( pBuf );

        return ret;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    // 
    // append path in all platform
    // 
    int misc_impl::path_append( char * lpszPath, int nBufSize, const char * lpszAdder )
    {
        return mgr::container()->str_append( lpszPath, nBufSize, lpszAdder, '/' );
    }

    int misc_impl::path_append( xos_box::i_string * pStr, xos_box::i_string * pAdder )
    {
        int ret = 0;
        pStr->append( pAdder, '/' );
        return ret;
    }

    int misc_impl::path_append( xos_box::i_string * pStr, const char * lpszAdder )
    {
        int ret = 0;
        pStr->append( lpszAdder, '/');
        return ret;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    // 
    // get real path.
    // 
    int misc_impl::real_path( const char * lpszPath, char * lpszRealPath, int nSize )
    {
        int ret = 0;
        const char * lpszRet = 0;
        if( lpszRealPath )
        {
            lpszRet = realpath( lpszPath, lpszRealPath );
        }
        if( !lpszRet )
        {
            ret = 1;
        }
        return ret;
    }

    int misc_impl::real_path( xos_box::i_string * pPath )
    {
        int ret = 0;

        xos_box::i_big_buf * pBuf = mgr::container()->box()->big_buf();
        char * pB = pBuf->get_data( 0, 0, 0, 0 );
        {
            const char * lpszRet = realpath( pPath->get( 0, 0 ), pB );
            if( !lpszRet )
            {
                ret = 1;
            }
        }
        if( 0 == ret )
        {
            pPath->set( pB );
        }
        xos_stl::release_interface( pBuf );

        return ret;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    // 
    // create guid
    // 
    int misc_impl::create_guid( char * lpszGuid )
    {
        int nRet = 0;

        if( lpszGuid )
        {
            uuid_t uuid = { 0 };
            uuid_generate( uuid );
            uuid_unparse_lower( uuid, lpszGuid );
        }

        return nRet;
    }

    // 
    // sleep 1 / 1000 second
    // 
    int misc_impl::sleep( int nMilliseconds )
    {
        int nRet = 0;
        usleep( 1000 * nMilliseconds );
        return nRet;
    }

    // 
    // output debuf info
    // 
    int misc_impl::out_put_debug_string( const char * lpszInfo )
    {
        int nRet = 0;
        return nRet;
    }

    int misc_impl::out_put_to_cmd( const char * lpszInfo )
    {
        using namespace std;
        int nRet = 0;
        cout << lpszInfo << "\r\n";
        return nRet;
    }

    int misc_impl::trace( const char * lpszFormat, ... )
    {
        using namespace std;

        int ret = 0;

        xos_box::i_big_buf * pBuf = mgr::container()->box()->big_buf();
        char * pB = pBuf->get_data( 0, 0, 0, 0 );
        int nSize = pBuf->get_buf_size( 0 );

        {
            va_list  va;
            va_start( va, lpszFormat );
            ret = vsnprintf( pB, nSize, lpszFormat, va );
            va_end( va );
        }
        {
            cout << pB << endl;
        }

        xos_stl::release_interface( pBuf );

        return ret;
    }

    // 
    // get seconds from since 1970-1-1
    // 
    uint64_t misc_impl::get_wall_second( uint64_t * lpWallSecond )
    {
        uint64_t xRet = ( uint64_t )time( 0 );
        *lpWallSecond = xRet;
        return xRet;
    }

    // 
    // get process ID
    // 
    uint64_t misc_impl::get_current_process_id()
    {
        uint64_t xRet = 0;
        return xRet;
    }

    // 
    // get thread ID
    // 
    uint64_t misc_impl::get_current_thread_id()
    {
        uint64_t xRet = ( uint64_t )pthread_self();
        return xRet;
    }

    const char * misc_impl::platform()
    {
        return os::platform();
    }

    bool misc_impl::can_hot_replace()
    {
        return os::can_hot_replace();
    }

    // 
    // get cpu num
    // 
    int misc_impl::get_cpu_num()
    {
        int nNum = 1;
        int nRet = 0;

        const int N = 100 * 1024;
        char buf[N] = { 0 };
        int nLen = 0;

        FILE * fp = 0;

        if( 0 == nRet )
        {
            fp = fopen( "/proc/cpuinfo", "rb" );

            if( !fp )
            {
                nRet = 1;
            }
        }

        if( 0 == nRet )
        {
            nLen = fread( buf, 1, N, fp );

            fclose( fp );
            fp = 0;

            if( nLen <= 0 )
            {
                nRet = 1;
            }
            else
            {
                buf[nLen] = 0;
            }
        }

        if( 0 == nRet )
        {
            int n = 0;
            const char * p = buf;

            while( 1 )
            {
                p = ::strstr( p, "processor" );

                if( p )
                {
                    p++;
                    n++;
                }
                else
                {
                    break;
                }
            }

            if( n > 0 )
            {
                nNum = n;
            }
        }

        return nNum;
    }

    // 
    // ensure_memory_visibility
    // 
    int misc_impl::ensure_memory_visibility()
    {
        int ret = os::ensure_memory_visibility();
        return ret;
    }

    // 
    // aligned alloc & free
    // 
    void* misc_impl::aligned_alloc( size_t size, size_t alignment )
    {
        void* pRet = 0;
        xos_stl::lock_free::aligned_alloc_and_free obj;
        pRet = obj.aligned_alloc( size, alignment );
        //posix_memalign( &pRet, alignment, size );
        return pRet;
    }

    void misc_impl::aligned_free( void* ptr )
    {
        xos_stl::lock_free::aligned_alloc_and_free obj;
        obj.aligned_free( ptr );
        //free( ptr );
    }

} // xos
